
#define	M_SIZE_INT			4096
#define M_SIZE_FLOAT		4096.
#define	M_ARRAY_SIZE		4096
#define M_COS_SIZE			4096
#define M_SPREADOVER_LENGTH 16384
#define M_MAX_AMP_VECTORS	8

#define	TEST 0

#include "ext.h"							// standard Max include, always required (except in Jitter)
#include "ext_obex.h"						// required for new style objects
#include "z_dsp.h"							// required for MSP objects
#include "buffer.h"
#include "ext_globalsymbol.h"
#include "ext_common.h"
#include "ext_atomic.h"
#include "ext_sysparallel.h"
//#include <Accelerate/Accelerate.h>

typedef enum {
	ATK = 0, DEC, SUS, REL, AMP, FRQ //, RES
} env_listlens;
#define M_PARAM_NUM 6

typedef struct _polysynth
{
	t_pxobject		m_ob;
	double			*m_wtab;
	double			*m_cosine;
	t_symbol		*m_buf;
	void			*m_mute_out;
	void			*m_clock;

	char			m_autozero;
	char			m_alwayson;
	char			m_rolloff;
	char			m_additive;

	int				*m_listlen;
	int				m_voices;
	int				m_unison;
	
	double			*m_atk;
	double			*m_dec;
	double			*m_sus;
	double			*m_rel;
	
	double			*m_res;
	
	double			*m_atk_slope;
	double			*m_dec_slope;
	double			*m_rel_slope;
	double			*m_rel_quantum;		
	
	double			*m_next_atk_slope;
	double			*m_next_dec_slope;
	double			*m_next_sus;
	double			*m_next_rel_quantum;
	
	long			m_freqsmooth;
	long			m_ampsmooth;
	long			m_frsm_step;
	long			m_amsm_step;
	double			*m_frsm_quantum;
	double			*m_amsm_quantum[M_MAX_AMP_VECTORS];
	double			*m_freq_ctl;
	double			*m_prev_freq;
	double			*m_amp_ctl[M_MAX_AMP_VECTORS];
	double			*m_prev_amp[M_MAX_AMP_VECTORS];
	char			m_freq_new;
	char			m_amp_new;
	char			m_all_voices_off[SYSPARALLEL_MAX_WORKERS];
	
	long			m_amp_vectors;
	
	double			*m_gain;

	double			*m_tabpos;
	double			m_wt_quantum;
	char			*m_env_segm;
	
//	double			m_resonance;
//	double			m_sub_gain;
//	double			*m_z1;
//	double			*m_z2;
//	double			m_cos_step;
	
	long			m_nyquist;
	long			m_cutoff;
	long			m_filter_range;
	double			m_inv_filter_range;
	
	long			m_spreadover;
	long			m_spreadstart;
	
	char			m_fast;
	
	t_sample		m_prev_tab_val;
	
	double			m_prev_ingate_i;

	t_int			m_vs;
	t_float			m_sr;
	char			m_mute;
	char			m_inpoly;
	
	struct _polysynth_parallel *m_parallel_data;
	double				*m_outputs[SYSPARALLEL_MAX_WORKERS];
	t_sysparallel_task	*m_paralleltask;
	t_max_err			m_parallelerror;
	long				m_threadcount;
	long				m_next_threadcount;
	
	long			*m_spreadover_modulo;
	
} t_polysynth;

typedef struct _polysynth_parallel {
	t_polysynth	*polysynth;
	long		count;
	t_sample	*in1;
	t_sample	*in2;
	t_sample	*in3;
	t_sample	*in4;
	t_sample	*amps[M_MAX_AMP_VECTORS];
	long		p_amsm_step_return;
	long		p_frsm_step_return;
	double		p_prev_ingate_i_return;
} t_polysynth_parallel;

void polysynth_assist(t_polysynth *x, void *b, long m, long a, char *s);
void *polysynth_new(t_symbol *s, long argc, t_atom *argv);
t_max_err polysynth_setattr_spreadover(t_polysynth *x, void *attr, long ac, t_atom *av);
void polysynth_free(t_polysynth *x);
void polysynth_dsp(t_polysynth *x, t_signal **sp, short *count);

void polysynth_mute(t_polysynth *x);

void polysynth_atk(t_polysynth *x, t_symbol *s, int argc, t_atom *argv);
void polysynth_dec(t_polysynth *x, t_symbol *s, int argc, t_atom *argv);
void polysynth_sus(t_polysynth *x, t_symbol *s, int argc, t_atom *argv);
void polysynth_rel(t_polysynth *x, t_symbol *s, int argc, t_atom *argv);
void polysynth_amp(t_polysynth *x, t_symbol *s, int argc, t_atom *argv);
void polysynth_frq(t_polysynth *x, t_symbol *s, int argc, t_atom *argv);
//void polysynth_res(t_polysynth *x, t_symbol *s, int argc, t_atom *argv);
void polysynth_unison(t_polysynth *x, long l);


void polysynth_set(t_polysynth *x, t_symbol *s, long argc, t_atom *argv);
void polysynth_dblclick(t_polysynth *x);

void polysynth_create_def_tab(t_polysynth *x);
int polysynth_create_cust_tab(t_polysynth *x, t_symbol *buf, long start, long chan);

void polysynth_set_voices(t_polysynth *x);

t_max_err polysynth_cutoff_set(t_polysynth *x, t_object *attr, long argc, t_atom *argv);


t_int *polysynth_perform(t_int *w);
t_int *polysynth_perform__(t_int *w);
void polysynth_workerproc(t_sysparallel_worker *w);


void *polysynth_class;


int main(void)
{	
	t_class *c;
	
	c = class_new("aa.polysynth~", (method)polysynth_new, (method)polysynth_free, (long)sizeof(t_polysynth), 0L, A_GIMME, 0);
	
	class_addmethod(c, (method)polysynth_dsp,		"dsp",			A_CANT, 0);
	class_addmethod(c, (method)polysynth_assist,	"assist",		A_CANT, 0);
	class_addmethod(c, (method)polysynth_dblclick,	"dblclick",		A_CANT, 0);

	class_addmethod(c, (method)polysynth_atk,		"atk",			A_GIMME, 0); // try A_USURP_LOW ?
	class_addmethod(c, (method)polysynth_dec,		"dec",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_sus,		"sus",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_rel,		"rel",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_frq,		"frq",			A_GIMME, 0);
//	class_addmethod(c, (method)polysynth_res,		"res",			A_GIMME, 0);

	class_addmethod(c, (method)polysynth_amp,		"amp0",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_amp,		"amp1",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_amp,		"amp2",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_amp,		"amp3",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_amp,		"amp4",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_amp,		"amp5",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_amp,		"amp6",			A_GIMME, 0);
	class_addmethod(c, (method)polysynth_amp,		"amp7",			A_GIMME, 0);

	
	class_addmethod(c, (method)polysynth_unison,	"unison",		A_LONG, 0);

	class_addmethod(c, (method)polysynth_set,		"set",		A_GIMME, 0);

	CLASS_ATTR_LONG(c, "freqsmooth",	0,	t_polysynth,	m_freqsmooth);
	CLASS_ATTR_FILTER_MIN(c, "freqsmooth", 0);
	
	CLASS_ATTR_LONG(c, "magsmooth",		0,	t_polysynth,	m_ampsmooth);
	CLASS_ATTR_FILTER_MIN(c, "magsmooth", 0);

	CLASS_ATTR_CHAR(c, "autozero",		0,	t_polysynth,	m_autozero);
	CLASS_ATTR_FILTER_CLIP(c, "autozero", 0, 1);
	
	CLASS_ATTR_CHAR(c, "alwayson",		0,	t_polysynth,	m_alwayson);
	CLASS_ATTR_FILTER_CLIP(c, "alwayson", 0, 1);
	
	CLASS_ATTR_CHAR(c, "rolloff",	0,	t_polysynth,	m_rolloff);
	CLASS_ATTR_FILTER_CLIP(c, "rolloff", 0, 1);
	
	CLASS_ATTR_CHAR(c, "additive",		0,	t_polysynth,	m_additive);
	CLASS_ATTR_FILTER_CLIP(c, "additive", 0, 1);
	
	CLASS_ATTR_CHAR(c, "fast",			0,	t_polysynth,	m_fast);

	CLASS_ATTR_CHAR(c, "inpoly",		0,	t_polysynth,	m_inpoly);
	
//	CLASS_ATTR_DOUBLE(c, "resonance",	0,	t_polysynth,	m_resonance);
	CLASS_ATTR_FILTER_CLIP(c, "resonance", 0, 1);	
	
//	CLASS_ATTR_DOUBLE(c, "sub_gain",	0,	t_polysynth,	m_sub_gain);
	CLASS_ATTR_FILTER_MIN(c, "sub_gain", 0);	
	
	CLASS_ATTR_LONG(c, "cutoff",		0,	t_polysynth,	m_cutoff);
	CLASS_ATTR_ACCESSORS(c, "cutoff", NULL, polysynth_cutoff_set);
	
	CLASS_ATTR_LONG(c, "spreadover",	0,	t_polysynth,	m_spreadover);
	CLASS_ATTR_FILTER_CLIP(c, "spreadover", 1, 8);
	CLASS_ATTR_ACCESSORS(c, "spreadover", (method)NULL, (method)polysynth_setattr_spreadover);
	
	CLASS_ATTR_LONG(c, "spreadstart",	0,	t_polysynth,	m_spreadstart);
	CLASS_ATTR_FILTER_CLIP(c, "spreadstart", 1, 8);

	CLASS_ATTR_LONG(c, "threadcount",	0,	t_polysynth,	m_next_threadcount);
	CLASS_ATTR_FILTER_CLIP(c, "threadcount", 1, 64);

	class_register(CLASS_BOX, c);	
	class_dspinit(c);
	polysynth_class = c;

	post("polysynth~ %s %s\n", __DATE__, __TIME__);

	return 0;
}

t_max_err polysynth_cutoff_set(t_polysynth *x, t_object *attr, long argc, t_atom *argv)
{
	long l = atom_getlong(argv);
	x->m_cutoff = MAX(-1, l);
	x->m_filter_range = x->m_cutoff >= 0 ? CLIP(x->m_nyquist - x->m_cutoff, 0, x->m_nyquist) : 0;
	x->m_inv_filter_range = (x->m_filter_range > 0) ? 1 / x->m_filter_range : 0;
	return 0;
}

void polysynth_set_voices(t_polysynth *x)
{
	int i, len, min = M_ARRAY_SIZE, new_env_segm;
	for (i = 0; i < M_PARAM_NUM; i++) { 
		len = i == FRQ ? x->m_listlen[i] / x->m_unison : x->m_listlen[i];
		if (min > len)
			min = len;
	}
	
	new_env_segm = 
		(x->m_voices > 0 && x->m_env_segm[0] != 0 && x->m_env_segm[0] != 4) ? 1 : 0;
		
	for (i = x->m_voices; i < min; i++) {
		x->m_tabpos[i] = 0;
		x->m_env_segm[i] = new_env_segm;
		x->m_prev_amp[i] = x->m_amp_ctl[i];
		x->m_prev_freq[i] = x->m_freq_ctl[i];
	}

	x->m_voices = min;
}

void polysynth_unison(t_polysynth *x, long l)
{
	if (l >= 1)
		x->m_unison = MIN(l, M_ARRAY_SIZE);
	else
		object_error((t_object *) x, "bad argument for message unison");
	polysynth_set_voices(x);
}

void polysynth_atk(t_polysynth *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	double f;

	for (i = 0; i < argc; i ++) {
		switch (argv[i].a_type) {
			case A_LONG:
				f = MAX(argv[i].a_w.w_long, 0) / 1000.;
				break;
			case A_FLOAT:
				f = MAX(argv[i].a_w.w_float, 0) / 1000.;
				break;
			default:
				f = 0;
				break;
		}
		x->m_atk[i] = f;
		x->m_next_atk_slope[i] = f ? 1. / x->m_sr / f : 1;
	}
	x->m_listlen[ATK] = argc;
	polysynth_set_voices(x);
}

void polysynth_dec(t_polysynth *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	double f;
	
	for (i = 0; i < argc; i ++) {
		switch (argv[i].a_type) {
			case A_LONG:
				f = MAX(argv[i].a_w.w_long, 0) / 1000.;
				break;
			case A_FLOAT:
				f = MAX(argv[i].a_w.w_float, 0) / 1000.;
				break;
			default:
				f = 0;
				break;
		}
		x->m_dec[i] = f;
		if (i < x->m_listlen[SUS])
			x->m_next_dec_slope[i] = f ? (1 - x->m_next_sus[i]) / x->m_sr / f : 1 - x->m_next_sus[i];
	}	
	x->m_listlen[DEC] = argc;
	polysynth_set_voices(x);	
}

void polysynth_sus(t_polysynth *x, t_symbol *s, int argc, t_atom *argv)
{
	short i;
	double f;
	for (i = 0; i < argc; i ++) {
		switch (argv[i].a_type) {
			case A_LONG:
				f = argv[i].a_w.w_long;
				break;
			case A_FLOAT:
				f = argv[i].a_w.w_float;
				break;
			default:
				f = 0;
				break;
		}
		x->m_next_sus[i] = f;
		if (i < x->m_listlen[DEC])
			x->m_next_dec_slope[i] = x->m_dec[i] ? (1 - f) / x->m_sr / x->m_dec[i] : 1 - f;

	}
	x->m_listlen[SUS] = argc;
	polysynth_set_voices(x);	
}

void polysynth_rel(t_polysynth *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	double f;
	
	for (i = 0; i < argc; i ++) {
		switch (argv[i].a_type) {
			case A_LONG:
				f = MAX(argv[i].a_w.w_long, 0) / 1000.;
				break;
			case A_FLOAT:
				f = MAX(argv[i].a_w.w_float, 0) / 1000.;
				break;
			default:
				f = 0;
				break;
		}
		x->m_rel[i] = f;
		x->m_next_rel_quantum[i] = f ? 1.0 / x->m_sr / f : 1;
	}
	x->m_listlen[REL] = argc;
	polysynth_set_voices(x);
}

void polysynth_amp(t_polysynth *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
//	double f;
	
	int target = *((s->s_name) + 3) - '0';
	
	for (i = 0; i < argc; i ++) {
/*		switch (argv[i].a_type) {
			case A_LONG:
				f = argv[i].a_w.w_long;
				break;
			case A_FLOAT:
				f = argv[i].a_w.w_float;
				break;
			default:
				f = 0;
				break;
		}*/
		(x->m_amp_ctl[target])[i] = atom_getfloat(argv + i);
	}
	x->m_listlen[AMP] = argc;
	polysynth_set_voices(x);
	x->m_amp_new = 1;
}

void polysynth_frq(t_polysynth *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	double f;
	
	for (i = 0; i < argc; i ++) {
		switch (argv[i].a_type) {
			case A_LONG:
				f = argv[i].a_w.w_long;
				break;
			case A_FLOAT:
				f = argv[i].a_w.w_float;
				break;
			default:
				f = 0;
				break;
		}
		x->m_freq_ctl[i] = f;
	}
	x->m_listlen[FRQ] = argc;
	polysynth_set_voices(x);
	x->m_freq_new = 1;
}


/*
void polysynth_res(t_polysynth *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	double f;
	
	for (i = 0; i < argc; i ++) {
		switch (argv[i].a_type) {
			case A_LONG:
				f = MAX(argv[i].a_w.w_long, 0);
				break;
			case A_FLOAT:
				f = MAX(argv[i].a_w.w_float, 0);
				break;
			default:
				f = 0;
				break;
		}
		x->m_res[i] = f;
	}
	x->m_listlen[RES] = argc;
	polysynth_set_voices(x);
}
*/

void polysynth_set(t_polysynth *x, t_symbol *s, long argc, t_atom *argv)
{
	t_symbol *buf;
	long start, chan;
	if (argc) {
		buf = atom_getsym(argv);
		start = (argc > 0) ? atom_getlong(argv + 1) : 0;
		chan = (argc > 1) ? atom_getlong(argv + 2) - 1 : 0;
		polysynth_create_cust_tab(x, buf, start, chan);

	} else {
		globalsymbol_dereference((t_object *)x, x->m_buf->s_name, "buffer~");
		x->m_buf = NULL;
		polysynth_create_def_tab(x);
	}
}

void polysynth_create_cosine_tab(t_polysynth *x)
{	
	int i;
	double *cosine = x->m_cosine;
	
	for (i = 0; i < M_COS_SIZE + 1; i++) {
		cosine[i] = cos((double) i / M_COS_SIZE * TWOPI);
	}
}

void polysynth_create_def_tab(t_polysynth *x)
{	
	int i;
	double *wtab = x->m_wtab;

	for (i = 0; i < M_SIZE_INT + 1; i++) {
		wtab[i] = sin((double) i / M_SIZE_INT * TWOPI);
	}
}

int polysynth_create_cust_tab(t_polysynth *x, t_symbol *buf, long start, long chan)
{
	t_buffer *b;
	long i, wt_idx, length, nchans;
	t_sample *bsamps;
	double *wtab = x->m_wtab;
	
	if (b = (t_buffer *) globalsymbol_reference((t_object *)x, buf->s_name, "buffer~")) {
		if (x->m_buf)
			globalsymbol_dereference((t_object *)x, x->m_buf->s_name, "buffer~");
		x->m_buf = buf;
			
		nchans = b->b_nchans;
		chan = CLIP(chan, 0, nchans - 1);
		length = b->b_frames * nchans;
		start = MIN(start, b->b_frames);
		bsamps = b->b_samples;
				
		for (i = start * nchans, wt_idx = 0; i < length && wt_idx <= M_SIZE_INT; i += nchans, wt_idx ++)
			wtab[wt_idx] = bsamps[i + chan];
		for ( ; wt_idx < M_SIZE_INT + 1; wt_idx ++)
			wtab[wt_idx] = bsamps[chan];
			
		ATOMIC_DECREMENT(&b->b_inuse);
		
		return 0;
	} else
		return 1;
}

void polysynth_dblclick(t_polysynth *x)
{
	t_buffer *b;
	if ((x->m_buf) && (b = ((t_buffer *) globalsymbol_reference((t_object *)x, x->m_buf->s_name, "buffer~"))))
		object_method(b, gensym("dblclick"));
}

void polysynth_dsp(t_polysynth *x, t_signal **sp, short *count)
{
	int i, j;
	void *vect[21];
	x->m_vs = sp[0]->s_n;
	x->m_sr = sp[0]->s_sr;
	x->m_mute = 0;
	
	x->m_threadcount = x->m_next_threadcount;
	
	for (i = 0; i < x->m_threadcount; i++)
		x->m_outputs[i] = (double *) sysmem_resizeptr(x->m_outputs[i], x->m_vs * 8 * sizeof(double));

	for (i = 0; i < x->m_voices; i++) {
		x->m_next_atk_slope[i] = x->m_atk[i] ? 1. / x->m_sr / x->m_atk[i] : 1;
		x->m_next_dec_slope[i] = x->m_dec[i] ? (1 - x->m_next_sus[i]) / x->m_sr / x->m_dec[i] : 1 - x->m_next_sus[i];
	}
/*	
	for (i = 0; i < M_ARRAY_SIZE; i++)
		x->m_z1[i] = x->m_z2[i] = 0;

	x->m_cos_step = M_COS_SIZE / x->m_sr;
*/
	x->m_wt_quantum = fmod(M_SIZE_FLOAT / x->m_sr, M_SIZE_INT);
	x->m_nyquist = x->m_sr * 0.5;
	x->m_filter_range = x->m_cutoff >= 0 ? CLIP(x->m_nyquist - x->m_cutoff, 0, x->m_nyquist) : 0;
	x->m_inv_filter_range = (x->m_filter_range > 0) ? 1.0 / x->m_filter_range : 0;
//	if (!x->m_fast)
	
	x->m_parallel_data->in1 = sp[0]->s_vec;
	x->m_parallel_data->in2 = sp[1]->s_vec;
	x->m_parallel_data->in3 = sp[2]->s_vec;
	x->m_parallel_data->in4 = sp[3]->s_vec;
	x->m_parallel_data->count = x->m_threadcount;
	
	vect[0] = x;
	
	for (i = 4, j = 0; i < 4 + x->m_amp_vectors; i++, j++)
		x->m_parallel_data->amps[j] = sp[i]->s_vec;
	for (j = 0 ; j < 8; i++, j++)
		vect[j+1] = sp[i]->s_vec;
	


	
/*	dsp_add(polysynth_perform, 13, 
		x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, 
		sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec, sp[8]->s_vec, sp[9]->s_vec, sp[10]->s_vec, sp[11]->s_vec); */
	
	dsp_addv(polysynth_perform, 9, vect);
	
	
//	else
//		dsp_add(polysynth_perform_fast, 8, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec);

}


t_int *polysynth_perform(t_int *w)
{
	t_polysynth *x = (t_polysynth *)(w[1]);
	//	t_sample *ingate = (t_sample *)(w[2]);
	//	t_sample *infreqmul = (t_sample *)(w[3]);
	//	t_sample *infreqadd = (t_sample *)(w[4]);
	//	t_sample *insrc = (t_sample *)(w[5]);
	t_sample *outaddsmp0 = (t_sample *)(w[2]);
	t_sample *outaddsmp1 = (t_sample *)(w[3]);
	t_sample *outaddsmp2 = (t_sample *)(w[4]);
	t_sample *outaddsmp3 = (t_sample *)(w[5]);
	t_sample *outaddsmp4 = (t_sample *)(w[6]);
	t_sample *outaddsmp5 = (t_sample *)(w[7]);
	t_sample *outaddsmp6 = (t_sample *)(w[8]);
	t_sample *outaddsmp7 = (t_sample *)(w[9]);
//	long count = (long)(w[15]);
//	t_sample *amps[M_MAX_AMP_VECTORS];
	
	
	t_sample add_sample0, add_sample1, add_sample2, add_sample3, add_sample4, add_sample5, add_sample6, add_sample7;
	
	//	t_sample *outsubsmp = (t_sample *)(w[7]);
//	t_sample *outstate = (t_sample *)(w[14]);
	
	double *this_output;
	
	int vs = x->m_vs;
	
	t_max_err err;
	int i;
	char mute = 1;
	
	char *all_voices_off = x->m_all_voices_off;

	t_polysynth_parallel *p = x->m_parallel_data;

//	p.polysynth = x;
	long count = x->m_threadcount;
/*	p.in1 = (t_sample *)(w[2]);
	p.in2 = (t_sample *)(w[3]);
	p.in3 = (t_sample *)(w[4]);
	p.in4 = (t_sample *)(w[5]); */
	
	if (!x->m_paralleltask)
		x->m_paralleltask = sysparallel_task_new(p, (method)polysynth_workerproc, count);
	
	x->m_paralleltask->priority = SYSPARALLEL_PRIORITY_HIGH;
	
	sysparallel_task_data(x->m_paralleltask, p);
	
	if (x->m_inpoly)
		x->m_prev_ingate_i *= !x->m_mute;
	x->m_mute = 0;
	
	if (err = sysparallel_task_execute(x->m_paralleltask)) {
		if (!x->m_parallelerror) {
			object_error((t_object *) x, "uh uh, we're in trouble! nested parallel tasks!");
			x->m_parallelerror = TRUE;
		}
		while (vs--) {
			*outaddsmp0++ = 0;
			*outaddsmp1++ = 0;
			*outaddsmp2++ = 0;
			*outaddsmp3++ = 0;
			*outaddsmp4++ = 0;
			*outaddsmp5++ = 0;
			*outaddsmp6++ = 0;
			*outaddsmp7++ = 0;
		}
		
		return w + 10;
	}

	
	long this_sample_base = 0;

	while (vs--) {
		
		add_sample0 = 0;
		add_sample1 = 0;
		add_sample2 = 0;
		add_sample3 = 0;
		add_sample4 = 0;
		add_sample5 = 0;
		add_sample6 = 0;
		add_sample7 = 0;
		
		for (i = 0; i < count; i++) {
			this_output = x->m_outputs[i] + this_sample_base;
			add_sample0 += *this_output++;
			add_sample1 += *this_output++;
			add_sample2 += *this_output++;
			add_sample3 += *this_output++;
			add_sample4 += *this_output++;
			add_sample5 += *this_output++;
			add_sample6 += *this_output++;
			add_sample7 += *this_output;
		}
		
		*outaddsmp0++ = add_sample0;
		*outaddsmp1++ = add_sample1;
		*outaddsmp2++ = add_sample2;
		*outaddsmp3++ = add_sample3;
		*outaddsmp4++ = add_sample4;
		*outaddsmp5++ = add_sample5;
		*outaddsmp6++ = add_sample6;
		*outaddsmp7++ = add_sample7;
		
//		*outstate++ = 0;
		
		this_sample_base += 8;
	}
	
	
	for (i = 0; mute && i < count; i++)
		mute &= *all_voices_off++;
	
	if (mute && !x->m_mute) 
		clock_delay(x->m_clock, 0);
	
	x->m_prev_ingate_i = p->p_prev_ingate_i_return;
	x->m_frsm_step = p->p_frsm_step_return;
	x->m_amsm_step = p->p_amsm_step_return;
	x->m_amp_new = 0;
	x->m_freq_new = 0;
	
	return w + 10;
}

// each worker works upon a set of contiguos voices
void polysynth_workerproc(t_sysparallel_worker *w)
{	
	t_polysynth_parallel *parallel_data = w->data;
	t_polysynth *x = parallel_data->polysynth;
	long count = parallel_data->count;
	long id = w->id;
		
	t_sample *ingate = parallel_data->in1;
	t_sample *infreqmul = parallel_data->in2;
	t_sample *infreqadd = parallel_data->in3;
	t_sample *insrc = parallel_data->in4;
	
	t_sample *amps[M_MAX_AMP_VECTORS], this_amps_val[M_MAX_AMP_VECTORS];
	
	double *add_sample = x->m_outputs[id];
	
//	long threadcount = w->task->workercount;
	int intpos, this_voice;
	double amp[M_MAX_AMP_VECTORS], freq, pos, rempos;
	double freq_headroom, freqmul, freqadd;
	double this_gain_val, this_tabpos_val, this_gain_val_pow;
	char this_env_segm_val;
	double *this_atk_slope, *this_dec_slope, *this_sus, *this_rel_slope, *this_rel_quantum;
	double *this_next_atk_slope, *this_next_dec_slope, *this_next_sus, *this_next_rel_quantum;
	double *this_gain, *this_tabpos;
	char *this_env_segm;
	long *spreadover_modulo = x->m_spreadover_modulo;
	
	int vs = x->m_vs;
	char all_voices_off = x->m_all_voices_off[id];
	int voices = x->m_voices;
	
	if (voices < count)
		count = voices;
	
	long unison = x->m_unison, this_uni;
	double voices_per_thread = count > 0 ? (double) voices / count : 0;
	long voice_from = voices_per_thread * id;	// first voice
	long voice_until = voices_per_thread * (id + 1) * (id < voices); // last voice
	long freqbase = unison * voice_from;
//	long freqleap = unison * threadcount;
//	long freqskip = freqleap - unison;
	
	
	double prev_ingate_i = x->m_prev_ingate_i;
	double ingate_i, *this_add_sample, sub_sample;
	
	double *tabpos = x->m_tabpos + freqbase;
	
	double *wtab = x->m_wtab;
	double wt_quantum = x->m_wt_quantum;
	
	double *gain = x->m_gain + voice_from;
	
	char *env_segm = x->m_env_segm + voice_from;
	char autozero = x->m_autozero;
//	char alwayson = x->m_alwayson;
//	char rolloff = x->m_rolloff;
//	char additive = x->m_additive;
	

	
//	long spreadstart = x->m_spreadstart;
	long spreadbase;
//	long this_spread;
	
	long amp_vectors = x->m_amp_vectors, vect;
	
	
	double ampsmooth = x->m_ampsmooth;
	double amsm_step = x->m_amsm_step;
	double *amsm_quantum[M_MAX_AMP_VECTORS], *this_amsm_quantum[M_MAX_AMP_VECTORS];
	int amp_new = x->m_amp_new;
	
	double freqsmooth = x->m_freqsmooth;
	double frsm_step = x->m_frsm_step;
	double *frsm_quantum = x->m_frsm_quantum + freqbase, *this_frsm_quantum;
	int freq_new = x->m_freq_new;
	
//	char fast = x->m_fast;

	
	double *atk_slope = x->m_atk_slope + voice_from;
	double *next_atk_slope = x->m_next_atk_slope + voice_from;
	double *dec_slope = x->m_dec_slope + voice_from;
	double *next_dec_slope = x->m_next_dec_slope + voice_from;
	double *sus = x->m_sus + voice_from;
	double *next_sus = x->m_next_sus + voice_from;
	double *rel_slope = x->m_rel_slope + voice_from;
	double *rel_quantum = x->m_rel_quantum + voice_from;
	double *next_rel_quantum = x->m_next_rel_quantum + voice_from;
	
	double *amp_ctl[M_MAX_AMP_VECTORS], *this_amp_ctl[M_MAX_AMP_VECTORS];
	double *freq_ctl = x->m_freq_ctl + freqbase, *this_freq_ctl;
	double *this_prev_amp[M_MAX_AMP_VECTORS], *this_prev_freq;
	double *prev_amp[M_MAX_AMP_VECTORS];
	double *prev_freq = x->m_prev_freq + freqbase;
	
	double fullsample;
	long out_chan;

	
//	double *cosine = x->m_cosine;
//	double cos_step = x->m_cos_step;
//	double *res = x->m_res + id;
//	double *z1 = x->m_z1 + freqbase;
//	double *z2 = x->m_z2 + freqbase;
//	double *x1 = x->m_x1;
//	double *x2 = x->m_x2;
//	double *y1 = x->m_y1;
//	double *y2 = x->m_y2;
	
//	double z;
//	double sub_val;
//	double sub_gain = x->m_sub_gain;
	
	long nyquist = x->m_nyquist;
//	long filter_range = x->m_filter_range;
//	double inv_filter_range = x->m_inv_filter_range;
	
	
// the smoothing quanta are calculated once per vector
	
	for (vect = 0; vect < amp_vectors; vect++) {
		amsm_quantum[vect] = x->m_amsm_quantum[vect] + voice_from;
		amp_ctl[vect] = x->m_amp_ctl[vect] + voice_from;
		prev_amp[vect] = x->m_prev_amp[vect] + voice_from;
		amps[vect] = parallel_data->amps[vect];
	}
	
	
	if (amp_new && ampsmooth > 1 && !all_voices_off) {
		amsm_step = ampsmooth - 1;
		for (vect = 0; vect < amp_vectors; vect++) {
			this_amsm_quantum[vect] = amsm_quantum[vect];
			this_amp_ctl[vect] = amp_ctl[vect];
			this_prev_amp[vect] = prev_amp[vect];
		}

		for (this_voice = voice_from; this_voice < voice_until; this_voice ++) {
			for (vect = 0; vect < amp_vectors; vect++)
				*this_amsm_quantum[vect]++ = (*this_amp_ctl[vect]++ - *this_prev_amp[vect]++) / (double) ampsmooth;		
		}
	}
	
	if (freq_new && freqsmooth > 1 && !all_voices_off) {
		frsm_step = freqsmooth - 1;
		this_frsm_quantum = frsm_quantum;
		this_freq_ctl = freq_ctl;
		this_prev_freq = prev_freq;
//		long sines = voices * unison;

		for (this_voice = voice_from; this_voice < voice_until; this_voice ++) {
			for (this_uni = 0; this_uni < unison; this_uni++) {
				*this_frsm_quantum++ = (*this_freq_ctl++ - *this_prev_freq++) / (double) freqsmooth;
			}
		}
	}
	
	while (vs --) {
		ingate_i = *ingate ++;

		if ((ingate_i > 0 || !all_voices_off) && voice_from < voice_until) { // if a note is on, or the gate is turned on right now
			for (vect = 0; vect < amp_vectors; vect++) {
				this_prev_amp[vect] = prev_amp[vect];
				this_amp_ctl[vect] = amp_ctl[vect];
				this_amsm_quantum[vect] = amsm_quantum[vect];
				this_amps_val[vect] = *amps[vect];
			}
			
			this_prev_freq = prev_freq;
			
			this_env_segm = env_segm;
			this_atk_slope = atk_slope;
			this_next_atk_slope = next_atk_slope;
			this_dec_slope = dec_slope;
			this_next_dec_slope = next_dec_slope;
			this_sus = sus;
			this_next_sus = next_sus;
			this_rel_slope = rel_slope;
			this_rel_quantum = rel_quantum;
			this_next_rel_quantum = next_rel_quantum;
			this_gain = gain;
			this_tabpos = tabpos;
			
			this_freq_ctl = freq_ctl;
			
			this_frsm_quantum = frsm_quantum;
			
//			double *this_res = res;
//			double *this_z1 = z1;
//			double *this_z2 = z2;

//			double *this_x1 = x1;
//			double *this_x2 = x2;
//			double *this_y1 = y1;
//			double *this_y2 = y2;
			
//			double this_insrc_val = *insrc;
			
			sub_sample = 0;
			
			this_add_sample = add_sample;
			
			*this_add_sample++ = 0;
			*this_add_sample++ = 0;
			*this_add_sample++ = 0;
			*this_add_sample++ = 0;
			*this_add_sample++ = 0;
			*this_add_sample++ = 0;
			*this_add_sample++ = 0;
			*this_add_sample = 0;
			
			
			freqmul = *infreqmul;
			freqadd = *infreqadd;


// preliminary setup for amp and freq smoothing
			if (all_voices_off)
				amsm_step = frsm_step = 0;

			if (ingate_i == 0 && prev_ingate_i > 0) {
				for (this_voice = voice_from; this_voice < voice_until; this_voice ++) {
					*this_env_segm++ = 4;
					*this_rel_slope++ = *this_gain++ * *this_rel_quantum++;
				}
				this_env_segm = env_segm;
				this_gain = gain;
			} else if (ingate_i == -1 && prev_ingate_i > -1 && !all_voices_off) {
				for (this_voice = voice_from; this_voice < voice_until; this_voice ++) {
					*this_env_segm++ = 4;
					*this_rel_slope++ = *this_gain++ * 0.01;
					this_rel_quantum ++;
				}
				this_env_segm = env_segm;
				this_gain = gain;
			} else if (ingate_i > 0 && prev_ingate_i < 1) {
				for (this_voice = voice_from; this_voice < voice_until; this_voice ++) {
					*this_atk_slope++ = *this_next_atk_slope++;
					*this_dec_slope++ = *this_next_dec_slope++;
					*this_sus++ = *this_next_sus++;
					*this_rel_quantum++ = *this_next_rel_quantum++;
					*this_env_segm = 1;
					
					if (*this_env_segm == 0 && autozero) {
						for (this_uni = 0; this_uni < unison; this_uni++) {
							*this_tabpos++ = 0;
						}
					} else {
						this_tabpos += unison;
					}
					
					this_env_segm ++;
				}
				this_env_segm = env_segm;
				this_atk_slope = atk_slope;
				this_next_atk_slope = next_atk_slope;
				this_dec_slope = dec_slope;
				this_next_dec_slope = next_dec_slope;
				this_sus = sus;
				this_next_sus = next_sus;
				this_rel_slope = rel_slope;
				this_rel_quantum = rel_quantum;
				this_next_rel_quantum = next_rel_quantum;
				this_tabpos = tabpos;
			}

			all_voices_off = 1;

			for (this_voice = voice_from; this_voice < voice_until; this_voice ++) {
				
				this_env_segm_val = *this_env_segm;
				this_gain_val = *this_gain;

				if (this_env_segm_val) { // if envelope is running or a new event is on, calculate a sample

// calculate this sample's gain

					switch(this_env_segm_val) {
						case 1:
							this_gain_val += *this_atk_slope;
							if (this_gain_val >= 1) {
								this_gain_val = 1;
								this_env_segm_val = 2;
							}
							break;
						case 2:
							this_gain_val -= *this_dec_slope;
							if (this_gain_val <= *this_sus) {
								if (this_gain_val <= 0) {
									this_gain_val = 0;
									this_env_segm_val = 0;
								} else {
									this_gain_val = *this_sus;
									this_env_segm_val = 3;
								}
							}
							break;
						case 4:
							this_gain_val -= *this_rel_slope;
							if (this_gain_val <= 0) {
								this_gain_val = 0;
								this_env_segm_val = 0;
							}
							break;
					}

					if (*this_amp_ctl || (amsm_step && *this_amsm_quantum)) {
 						
						spreadbase = this_voice * unison;
						this_gain_val_pow = this_gain_val * this_gain_val * this_gain_val;

#if TEST
						amp = *this_amp_ctl - (*this_amsm_quantum) * amsm_step;
#else
						if (amsm_step) {
							for (vect = 0; vect < amp_vectors; vect++) {
								amp[vect] = *this_amp_ctl[vect] - (*this_amsm_quantum[vect]) * amsm_step;
								*this_prev_amp[vect] = amp[vect];
								amp[vect] *= this_gain_val_pow;
							}
						} else {
							for (vect = 0; vect < amp_vectors; vect++) {
								amp[vect] = *this_amp_ctl[vect];
								*this_prev_amp[vect] = amp[vect];
								amp[vect] *= this_gain_val_pow;
							}
						}

//						amp = amsm_step ? *this_amp_ctl - (*this_amsm_quantum) * amsm_step : *this_amp_ctl;
#endif
//						*this_prev_amp = amp;
//						amp *= this_gain_val * this_gain_val * this_gain_val;


						for (this_uni = 0; this_uni < unison; this_uni ++) {
							this_tabpos_val = *this_tabpos;

#if TEST
							freq = *this_freq_ctl - (*this_frsm_quantum) * frsm_step;
#else
							freq = frsm_step ? *this_freq_ctl - (*this_frsm_quantum) * frsm_step : *this_freq_ctl;
#endif
							*this_prev_freq = freq;
							freq = freq * freqmul + freqadd;


							if (freq > 0 && freq < nyquist) {
//								double weighted_amp = 
//									(!rolloff || (freq_headroom = nyquist - freq) >= filter_range ? amp : amp * freq_headroom * inv_filter_range);

// calculate the position in the table
//								if (additive) {
//									pos = this_tabpos_val;

									intpos = (int) this_tabpos_val;
//									intpos &= (M_SIZE_INT - 1);

									rempos = this_tabpos_val - intpos;

//									add_sample[(((spreadbase + this_uni) % spreadover) + spreadstart) & 7] += (wtab[intpos] * (1.0 - rempos) + wtab[intpos+1] * rempos) * amp;
									fullsample = (wtab[intpos] * (1.0 - rempos) + wtab[intpos+1] * rempos);
									out_chan = spreadover_modulo[spreadbase + this_uni];
									for (vect = 0; vect < amp_vectors; vect++)
										add_sample[out_chan] += fullsample * amp[vect] * this_amps_val[vect];
								
// put weighted_amp instead of amp if you want to implement rolloff!
							
// and update the position (this happens at the end, so that when the envelope is turned on the sample pointed by inphase is read)
									this_tabpos_val += freq * wt_quantum;
								
// since I check for freq to be > 0 and < nyquist, I can simply wrap the table this way
									if (this_tabpos_val >= M_SIZE_INT)
										this_tabpos_val -= M_SIZE_INT;
//									while (this_tabpos_val < 0)
//										this_tabpos_val += M_SIZE_INT;
																								
//								}

// calculate the subtractive synthesis
/*
								if (sub_gain) {
								
									double cosarg = freq * cos_step;
									int intcosarg = (int) cosarg;
									double remcosarg = cosarg - intcosarg;
									double cosval = cosine[intcosarg] * (1.0 - remcosarg) + cosine[intcosarg+1] * remcosarg;

									double this_res_val = *this_res;

									double this_z1_val = *this_z1;
									double this_z2_val = *this_z2;

									z = this_insrc_val + this_res_val * 
										(this_z1_val * cosval 
										 * 2 - this_res_val * this_z2_val);
									sub_sample += (1 - this_res_val * this_res_val) * 0.5 * (z - this_z2_val) * amp;

								
									*this_z2 = this_z1_val;
									*this_z1 = z;


								}
 */
							} // end of anti-aliasing if
					
							this_prev_freq ++;
							this_freq_ctl ++;
							*this_tabpos = this_tabpos_val;
							this_tabpos ++;
							this_frsm_quantum ++;
//							this_z1 ++;
//							this_z2 ++;
						}
					} else { // if this voice was quiet
						this_prev_freq += unison;
						this_freq_ctl += unison;
						this_tabpos += unison;	
						this_frsm_quantum += unison;

//						this_z1 += unison;
//						this_z2 += unison;
					}
					
					all_voices_off = 0;
				} else { // if envelope is done and no new event is on
					this_prev_freq += unison;
					this_freq_ctl += unison;
					this_tabpos += unison;	
					this_frsm_quantum += unison;

//					this_z1 += unison;
//					this_z2 += unison;
				}
				for (vect = 0; vect < amp_vectors; vect++) {
					this_prev_amp[vect] ++;
					this_amp_ctl[vect] ++;
					this_amsm_quantum[vect] ++;
				}
			
				*this_env_segm = this_env_segm_val;
				this_env_segm ++;
				
				this_atk_slope ++;
				this_next_atk_slope ++;
				this_dec_slope ++;
				this_next_dec_slope ++;
				this_sus ++;
				this_next_sus ++;
				this_rel_slope ++;
				this_rel_quantum ++;
				this_next_rel_quantum ++;
				*this_gain = this_gain_val;
				this_gain ++;
				
				
//				this_res += threadcount;

//				this_x1 ++;
//				this_x2 ++;
//				this_y1 ++;
//				this_y2 ++;

			}
			
			if (amsm_step)
				amsm_step --;
			if (frsm_step)
				frsm_step --;
	
//			this_add_sample = add_sample;
//			*outaddsmp0++ = *this_add_sample++;
//			*outaddsmp1++ = *this_add_sample++;
//			*outaddsmp2++ = *this_add_sample++;
//			*outaddsmp3++ = *this_add_sample++;
//			*outaddsmp4++ = *this_add_sample++;
//			*outaddsmp5++ = *this_add_sample++;
//			*outaddsmp6++ = *this_add_sample++;
//			*outaddsmp7++ = *this_add_sample++;
//			*outsubsmp++ = sub_sample * sub_gain;
//			*outstate++ = 1.0f;
		} else {
			this_add_sample = add_sample;
			*this_add_sample++ = 0.0f;
			*this_add_sample++ = 0.0f;
			*this_add_sample++ = 0.0f;
			*this_add_sample++ = 0.0f;
			*this_add_sample++ = 0.0f;
			*this_add_sample++ = 0.0f;
			*this_add_sample++ = 0.0f;
			*this_add_sample = 0.0f;


//			*outsubsmp++ = 0.0f;
//			*outstate++ = 0.0f;
		}
		infreqmul ++;
		infreqadd ++;
		insrc ++;
		
		for (vect = 0; vect < amp_vectors; vect++)
			amps[vect]++;
		
		prev_ingate_i = ingate_i;
		add_sample += 8;
	}
	
	x->m_all_voices_off[id] = all_voices_off;
	if (id == 0) {
		((t_polysynth_parallel *)(w->data))->p_amsm_step_return = amsm_step;
		((t_polysynth_parallel *)(w->data))->p_frsm_step_return = frsm_step;
		((t_polysynth_parallel *)(w->data))->p_prev_ingate_i_return = prev_ingate_i;
	}

}




void polysynth_assist(t_polysynth *x, void *b, long m, long a, char *s)
{
	if (m == ASSIST_INLET)
		switch(a) {
			case 0:	sprintf(s, "gate");				break;
			case 1:	sprintf(s, "transposition");	break;
			case 2:	sprintf(s, "freq shift");		break;
			case 3: sprintf(s, "audio input");		break;
			default: sprintf(s, "amplitude of vector %ld", a - 3);	break;
		}
	else
		switch(a) {
/*			case 0:	sprintf(s, "additive output");		break;
			case 1:	sprintf(s, "subtractive output");	break;
			case 2: sprintf(s, "active?");				break; */
			case 8: sprintf(s, "active?");				break;
			default: sprintf(s, "additive output %ld", a + 1);	break;
		}
}

void polysynth_free(t_polysynth *x) 
{
	dsp_free((t_pxobject *) x);
	
	freeobject((t_object *) x->m_clock);
	
	if (x->m_paralleltask)
		sysparallel_task_free(x->m_paralleltask);
	
	sysmem_freeptr(x->m_spreadover_modulo);
	sysmem_freeptr(x->m_wtab);
	sysmem_freeptr(x->m_cosine);
	sysmem_freeptr(x->m_atk);
	sysmem_freeptr(x->m_dec);
	sysmem_freeptr(x->m_sus);
	sysmem_freeptr(x->m_rel);
	
	sysmem_freeptr(x->m_res);
	
	sysmem_freeptr(x->m_atk_slope);
	sysmem_freeptr(x->m_dec_slope);
	sysmem_freeptr(x->m_rel_slope);
	sysmem_freeptr(x->m_rel_quantum);
	
	sysmem_freeptr(x->m_next_atk_slope);
	sysmem_freeptr(x->m_next_dec_slope);
	sysmem_freeptr(x->m_next_sus);
	sysmem_freeptr(x->m_next_rel_quantum);
	
	sysmem_freeptr(x->m_frsm_quantum);
	sysmem_freeptr(x->m_amsm_quantum);
	
	sysmem_freeptr(x->m_freq_ctl);
	sysmem_freeptr(x->m_prev_freq);
	sysmem_freeptr(x->m_amp_ctl);
	sysmem_freeptr(x->m_prev_amp);
	
	sysmem_freeptr(x->m_gain);
	
	sysmem_freeptr(x->m_tabpos);
	sysmem_freeptr(x->m_env_segm);
	
	sysmem_freeptr(x->m_outputs);
	
	sysmem_freeptr(x->m_parallel_data);

	
}

void polysynth_mute(t_polysynth *x)
{
	outlet_bang(x->m_mute_out);
	x->m_mute = 1;
}


/*
optional argument: amp_vectors
*/
void *polysynth_new(t_symbol *s, long argc, t_atom *argv)
{
	t_polysynth *x = NULL;
	long ac;
	int i;
	
	if (x = (t_polysynth *)object_alloc(polysynth_class)) {
		if (!(x->m_wtab = (double *) sysmem_newptr((M_SIZE_INT + 1) * sizeof (double)))) {
			object_error((t_object *) x, "Memory allocation problem!");
			return 0;
		}
		
		if (!(x->m_cosine = (double *) sysmem_newptr((M_COS_SIZE + 1) * sizeof (double)))) {
			object_error((t_object *) x, "Memory allocation problem!");
			return 0;
		}
		
		x->m_buf = NULL;
		
		ac = attr_args_offset(argc, argv);
/*		
		switch (ac) {
			case 1:
				if (polysynth_create_cust_tab(x, atom_getsym(argv), 0, 0))
					polysynth_create_def_tab(x);
				break;
			case 0:
				polysynth_create_def_tab(x);
				break;
			default:
				object_warn((t_object *) x, "wrong number of arguments - setting to defaults!");
				polysynth_create_def_tab(x);
				break;
		}
*/		
		polysynth_create_def_tab(x);
		polysynth_create_cosine_tab(x);

		if (ac)
			x->m_amp_vectors = atom_getlong(argv);
		
		x->m_amp_vectors = CLIP(x->m_amp_vectors, 1, M_MAX_AMP_VECTORS);
			
		
		x->m_autozero = 0;
		x->m_alwayson = 0;
		x->m_freqsmooth = x->m_ampsmooth = 0;
		x->m_cutoff = -1;
		x->m_unison = 1;
		x->m_next_threadcount = sysparallel_processorcount();
		x->m_spreadover_modulo = (long *)sysmem_newptr(M_SPREADOVER_LENGTH * sizeof(long));
		x->m_spreadover = 0;	// just to make sure that the setter fills the array properly (not really required)
		object_attr_setlong(x, gensym("spreadover"), 8);
		x->m_spreadstart = 0;

		attr_args_process(x, argc, argv);
		
		x->m_voices = 0;
		
		
		
		if (!(
			(x->m_atk = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) && 
			(x->m_dec = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_sus = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_rel = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			
			(x->m_atk_slope = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_dec_slope = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_rel_slope = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_rel_quantum = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			
			(x->m_next_atk_slope = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_next_dec_slope = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_next_sus = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_next_rel_quantum = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&

			(x->m_frsm_quantum = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
//			(x->m_amsm_quantum = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			
			(x->m_freq_ctl = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_prev_freq = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
//			(x->m_amp_ctl = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
//			(x->m_prev_amp = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			
			(x->m_res = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			
			(x->m_gain = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			
			(x->m_tabpos = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			(x->m_env_segm = (char *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(char))) &&
			(x->m_listlen = (int *) sysmem_newptrclear(M_PARAM_NUM * sizeof(int))) &&
			(x->m_parallel_data = (t_polysynth_parallel *) sysmem_newptrclear(sizeof(t_polysynth_parallel))) &&
			

//			(x->m_z1 = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
//			(x->m_z2 = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double))) &&
			  1)) {
			object_post((t_object *)x, "AAARGH!");
			return NULL;
		}
		
		for (i = 0; i < M_MAX_AMP_VECTORS; i++) {
			x->m_amsm_quantum[i] = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double));
			x->m_amp_ctl[i] = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double));
			x->m_prev_amp[i] = (double *) sysmem_newptrclear(M_ARRAY_SIZE * sizeof(double));
		}
		
		for (i = 0; i < SYSPARALLEL_MAX_WORKERS; i++) {
			x->m_outputs[i] = NULL;
			x->m_all_voices_off[i] = 1;
		}
		
		x->m_clock = clock_new(x, (method)polysynth_mute);
		
		x->m_sr = sys_getsr();
		x->m_nyquist = x->m_sr * 0.5;
		x->m_frsm_step = x->m_amsm_step = 0;
		x->m_prev_ingate_i = 0;
		x->m_parallelerror = MAX_ERR_NONE; 
//		x->m_fast = 1;
		

		
		x->m_paralleltask = NULL;
		x->m_parallel_data->polysynth = x;

		dsp_setup((t_pxobject *)x, 4 + x->m_amp_vectors); // gate, freq_mul, freq_add, sound_in, amplitudes
		((t_pxobject *)x)->z_misc &= Z_PUT_LAST;
//		outlet_new((t_object *) x, "signal");  // state
//		outlet_new((t_object *) x, "signal");  // subtractive out
		x->m_mute_out = bangout((t_object *) x);
		outlet_new((t_object *) x, "signal");  // additive out
		outlet_new((t_object *) x, "signal");  // additive out
		outlet_new((t_object *) x, "signal");  // additive out
		outlet_new((t_object *) x, "signal");  // additive out
		outlet_new((t_object *) x, "signal");  // additive out
		outlet_new((t_object *) x, "signal");  // additive out
		outlet_new((t_object *) x, "signal");  // additive out
		outlet_new((t_object *) x, "signal");  // additive out

	} else
		object_error((t_object *) x, "couldn't instantiate polysynth~");

	return (x);
}

t_max_err polysynth_setattr_spreadover(t_polysynth *x, void *attr, long ac, t_atom *av)
{
	if (ac && av) {
		long old_spreadover = x->m_spreadover;
		long news_spreadover = atom_getlong(av);
		news_spreadover = CLIP(news_spreadover, 1, 8);
		
		if (old_spreadover != news_spreadover) {
			long i, *item;
			
			x->m_spreadover = news_spreadover;
			item = x->m_spreadover_modulo;
			for (i = 0; i < M_SPREADOVER_LENGTH; i++, item++)
				*item = i % news_spreadover;
		}
	}
	return MAX_ERR_NONE;
}
